#include <iostream>
#include <vector>
#include<cstring>
#include <string>
#include <cerrno>
#include <cstdbool>
#include <unistd.h>
#include <sys/types.h>
#include<sys/wait.h>
#include "task.hpp"

// 先描述：
class Channel
{
public:
    // 构造函数 + 初始化列表：
    Channel(int wid, pid_t processId, std::string name)
        : _wid(wid),
          _processId(processId),
          _name(name) {}

    int GetWid() const
    {
        return _wid;
    }
    pid_t GetProcessId() const
    {
        return _processId;
    }
    std::string GetName() const
    {
        return _name;
    }
    void CloseChannel() const
    {
        close(_wid);
    }
    void Wait() const
    {
        int status = 0;
        pid_t wid = waitpid(_processId, &status, 0);
        if(wid < 0)
        {
            std::cout << "Wait false: Check the processId " << _processId << " if exists?" << std::endl;
            exit(errno);  
        }
        else
        {
            if(WIFEXITED(status))
            {
                int tmp = WEXITSTATUS(status);
                printf("wait %d: errno == %d-> %s\n", wid, tmp, strerror(tmp));
            }
            else
                std::cout << "The child process adnormally quit. exit_signal: " << (status & 0x7f) << std::endl;
        }
    }
private:
    int _wid;
    pid_t _processId;
    std::string _name;
};

// Ⅰ若干个子函数：
void work(int rid)
{
    while (true)
    {
        int taskid = 0;
        int n = read(rid, &taskid, sizeof taskid);
        if(n > 0)
        {
            ExcuteTask(taskid);
            std::cout << "==============================" << std::endl;
        }
        else if(n == 0) 
        {
            std::cout << "child process: " << getpid() << " quit!" << std::endl;
            break;
        }
    }
}
// Ⅱ若干个子模块：
/*形参的命名规范：*/
// const &:输入型参数；
//&:输入输出型参数；
//*:输出型参数
void CreateChannel(int num, std::vector<Channel> &channels)
{
    for (int i = 0; i < num; i++)
    {
        // 1.创建管道：
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if (n < 0)
        {
            std::cout << "Errno: You can't create so many pipes." << std::endl;
            exit(errno);
        }
        // 2.创建子进程：
        pid_t id = fork();
        if (id == 0)
        {
            // child: read
            close(pipefd[1]);
            work(pipefd[0]);
            close(pipefd[0]);
            exit(errno);
        }
        // father
        //  The pid of child && The wid of pipe:
        close(pipefd[0]);
        std::string channel_name = "Channel-" + std::to_string(i);
        channels.push_back(Channel(pipefd[1], id, channel_name));
    }
}
int SelectChannel(int channelNum)
{
    static int id = 0;
    id %= channelNum;
    return id++;
}
void SendTask(const Channel& channel, int taskid)
{
    std::cout << channel.GetName() << std::endl;
    std::cout << "processID:" << channel.GetProcessId() << std::endl;
    std::cout << "wid:" << channel.GetWid() << std::endl;
    std::cout << "TaskId->" << taskid << std::endl;
    write(channel.GetWid(), &taskid, sizeof taskid);
}
void ControlProcess(const std::vector<Channel>& channels, int times = 5)
{
    while (times--)
    {
        sleep(1);
        // a.选中一个任务：
        int taskid = SelectTask();
        // b.选中一个信道和进程：
        int chanid = SelectChannel(channels.size());
        // c.发送任务
        SendTask(channels[chanid], taskid);
    }
}
//回收进程和管道资源（这里有坑：一定要关闭所有wid后，再等待解决僵尸问题；不能一边关wid一边wait!）：
void CleanSource(const std::vector<Channel>& channels)
{
    //写法一：
    // //1.先回收所有的管道资源：
    // for(auto& k : channels)
    //     k.CloseChannel();
    // //2.再回收所有的进程：
    // for(auto& k : channels)
    //     k.Wait();
    //写法二：
    int num = channels.size() - 1;
    while(num >= 0)
    {
        channels[num].CloseChannel();
        channels[num--].Wait();
    }
}
int main(int argc, char *argv[])
{
    // 再组织：以顺序表的形式组织起来
    std::vector<Channel> channels;
    // 检查是否是正确的输入：
    if (argc != 2)
    {
        std::cout << "Usage: " << argv[0] << "-[the Numbers of Processn]" << std::endl;
        exit(1);
    }
    if (argv[1][0] != '-')
    {
        std::cout << "Usage: " << "minus sign: -" << std::endl;
        exit(1);
    }

    int num = atoi(argv[1] + 1);
    LoadTask();
    // 1. 创建信道和子进程：
    CreateChannel(num, channels);

    // 2. 通过Channel来控制子进程：
    ControlProcess(channels);

    //3.回收管道资源和子进程：
    CleanSource(channels);
    return 0;
}